package com.pengke.pangu.aspect;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.json.JSONUtil;
import com.pengke.pangu.annotation.SLog;
import com.pengke.pangu.utils.IPUtil;
import com.pengke.pangu.service.sys.entity.SysLog;
import com.pengke.pangu.service.sys.entity.SysUser;
import com.pengke.pangu.service.sys.service.ISysLogService;
import com.pengke.pangu.service.sys.service.ISysUserService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.lang.reflect.Method;
import java.util.Objects;

@Aspect
@Component
@Slf4j
public class SLogAspect {

    @Resource
    private ISysLogService sysLogService;

    @Resource
    private ISysUserService sysUserService;

    private Long sLogStartTime;

    private DateTime opDatetime;

    @Pointcut("@annotation(com.pengke.pangu.annotation.SLog)")
    public void sysLogPointCut(){

    }

    // 通知- 前置通知： 记录方法被调用的时间，方法执行时间计时器开始
    @Before("sysLogPointCut()")
    public void beforeMethod(JoinPoint point){
        this.sLogStartTime = System.currentTimeMillis();
        opDatetime = DateTime.now();
    }

    // 执行成功后通知
    @AfterReturning(value = "sysLogPointCut()", returning = "result")
    public void afterReturn(JoinPoint joinPoint, Object result){
        // 获取当前时间和开始时间比较 得到方法的执行时间
        long execTime = System.currentTimeMillis() - this.sLogStartTime;
        handleSysLog(true,joinPoint,result,execTime,null);
    }

    // 异常后的通知
    @AfterThrowing(value = "sysLogPointCut()",throwing = "e")
    public void afterThrowing(JoinPoint joinPoint,Exception e){
        long execTime = System.currentTimeMillis() - this.sLogStartTime;
        handleSysLog(false,joinPoint,null,execTime,e);
    }

    protected void handleSysLog(boolean status,JoinPoint joinPoint, Object result,Long execTime,Exception e){

        //获取用户ID
        int userId = Integer.parseInt(StpUtil.getLoginId().toString());
        //获取用户名
        SysUser user = sysUserService.getById(userId);
        // 在spring容器中快速获取当前线程的请求对象
        HttpServletRequest httpServletRequest = (HttpServletRequest) Objects.requireNonNull(RequestContextHolder.getRequestAttributes()).resolveReference(RequestAttributes.REFERENCE_REQUEST);
        // 获取aop拦截的方法 - class字节码对象
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // aop拦截的方法字节码对象
        Method method = signature.getMethod();

        SysLog sysLog = new SysLog();

        sysLog.setMethodName(method.getName());
        if (httpServletRequest != null) {
            sysLog.setMethodType(httpServletRequest.getMethod());
        }
        sysLog.setOpUserId(userId);
        sysLog.setOpUserName(user.getAccount());
        sysLog.setOpIp(IPUtil.getIpAddr(httpServletRequest));
        if (httpServletRequest != null) {
            sysLog.setOpUrl(httpServletRequest.getRequestURI());
        }
        sysLog.setOpParam(JSONUtil.toJsonStr(joinPoint.getArgs()));
        sysLog.setOpDatetime(opDatetime);
        sysLog.setOpExecTime(execTime);

        // 获取当前方法头部的@MySysLog注解
        SLog annotation = method.getAnnotation(SLog.class);
        if(annotation!=null) { // 方法头部编写了该注解
            sysLog.setModel(annotation.model());
            sysLog.setBizType(annotation.bizType().toString());
        }

        //成功和异常分别封装相关数据
        if(status){
            sysLog.setOpStatus(1);
        }else{
            sysLog.setOpStatus(0);
            sysLog.setOpErrorMsg(e.getMessage());
        }

        sysLogService.save(sysLog);
    }
}
